package com.itbaizhan.orders.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.itbaizhan.orders.entity.Orders;
import com.itbaizhan.orders.entity.TxLog;
import com.itbaizhan.orders.mapper.OrderMapper;
import com.itbaizhan.orders.mapper.TxLogMapper;
import com.itbaizhan.orders.service.IOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itbaizhan.orders.tx.TxMessage;

import org.apache.rocketmq.spring.core.RocketMQTemplate;

import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.UUID;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author itbaizhan
 * @since 07-11
 */

//发送消息到rocketmq,发送的是事务消息，接收rocketmq返回的消息
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Orders> implements IOrderService {

    @Resource
    private RocketMQTemplate rocketMQTemplate;
    @Resource
    private TxLogMapper txLogMapper;



    /*1,订单微服给rocketmq发送消息，为了让订单微服知道rocketmq接收到消息从而执行本地事务，那就
    * 要创建监听器OrderTxMessageListener，监听器首先要把消息取出来，消息能取出来，说明消息在rocketmq保存了，但是未确认状态,
    * 2,写一个保存订单的方法save()
    * 3,写一个用于提交订单同时解决幂等问题
    * 4当订单写到本地事务，没有发生异常，return RocketMQLocalTransactionState.COMMIT;
    * 消息由未确认转为已确认，这样rocketmq才可以发送消息到消费者
    * 如果发生异常 return RocketMQLocalTransactionState.ROLLBACK;
    * */
    /**
     * 提交订单
     * @param productId 商品编号
     * @param payCount 购买数量
     */
    @Override
    public void submitOrders(Long productId, Integer payCount) {

        //1，生成全局分布式事务编号，replace("-","") 将 - 转为“ ”
        String txNo = UUID.randomUUID().toString().replace("-","");

        //2,封装消息
        TxMessage txMesage = new TxMessage();
        txMesage.setTxNo(txNo);
        txMesage.setPayCount(payCount);
        txMesage.setProductId(productId);

        //3,转为json
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("message",txMesage); //设为key - value  那么消息消费模块就能通过key拿到value

        Message<String> message = MessageBuilder.withPayload(jsonObject.toJSONString()).build();
        //发送事务消息，消息不允许消费
        //参数： 自定义组名   自定义主题    消息
        rocketMQTemplate.sendMessageInTransaction("tx_order_group","topic_txmsg",message,null);


    }

    /**
     * 添加订单
     * @param productId 商品编号
     * @param payCount 购买数量
     */
    @Override
    public void save(Long productId, Integer payCount) {

        Orders order = new Orders();
        order.setCreateTime(LocalDateTime.now());
        order.setOrderNo(UUID.randomUUID().toString().replace("-",""));
        order.setPayCount(payCount);
        order.setProductId(productId);

        baseMapper.insert(order);

    }

    //提交订单同时解决幂等问题
    //保证添加订单与添加事务的原子性
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void sumbitOrderAndSaveTxNo(TxMessage txMessage) {
        //判断事务是否处理过
        //1根据事务id,查询事务
        TxLog txLog = txLogMapper.selectById(txMessage.getTxNo());
        //2如果 事务日志不为空，说明已经发送过一次
        if(txLog != null){
            return;
        }

       // 3，生成订单
        this.save(txMessage.getProductId(),txMessage.getPayCount());

        //如果没有执行过，就添加事务日志
        TxLog txLog1 = new TxLog();
        txLog1.setTxNo(txMessage.getTxNo());
        txLog1.setCreateTime(LocalDateTime.now());

        txLogMapper.insert(txLog1);
    }
}
